/**@file    sys_posix.h
 * @note   
 */

#include "app_common.h"
#include <signal.h>
#include <sys/types.h>
#include <mqueue.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <stdarg.h>
#include "sys_posix.h"

#define MSG_Q_OFLAG (O_CREAT | O_RDWR | O_EXCL)
#define MSG_Q_MODE (0666)
#define MSG_Q_PRIO (0)
#define NSEC2SEC (1000000 * 1000) //纳秒到秒的换算

typedef struct mq_attr MSG_Q_ATTR;

typedef struct
{
    FUNCPTR entry; /* execution entry point address for thread */
    char *pName;
    void *arg[10]; /* arguments */
} FUNC_WRAPPER3;

static void *sys_thread_wrapper(void *arg)
{
    FUNC_WRAPPER3 stfunc = (*(FUNC_WRAPPER3 *)arg);

    free(arg);

    if (stfunc.pName != NULL)
    {
        (void)prctl(15, stfunc.pName);
    }

    stfunc.entry(stfunc.arg[0], stfunc.arg[1], stfunc.arg[2], stfunc.arg[3], stfunc.arg[4],
                 stfunc.arg[5], stfunc.arg[6], stfunc.arg[7], stfunc.arg[8], stfunc.arg[9]);

    return NULL;
}

/*
 * Function: getTimespec
 * Description: Get the time spec struct.
 * Input:   wait_ms - NO_WAIT, or max wait time(ms)
 * Output:  tp - time spec struct
 * Return:  none
 *
 */
static inline void sys_timespec_get(INT32 iWaitMs, struct timespec *tp)
{
    time_t sec;
    long long int nsec;

    if (iWaitMs == NO_WAIT)
    {
        nsec = 0;
    }
    else
    {
        nsec = iWaitMs;
        nsec *= 1000000LL;
    }

#ifdef USE_NPTL
    tp->tv_sec = 0;
#else
    if (clock_gettime(CLOCK_REALTIME, tp) == -1)
    {
        PRINT_DEBUG("getTimespec: clock_gettime call fail, error (%s)\n", strerror(errno));
        tp->tv_sec = time(NULL) + 1;
        tp->tv_nsec = 0;
    }
    else
    {
        sec = time(NULL) + 1;
        if (abs(tp->tv_sec - sec) > 30)
        {
            tp->tv_sec = sec;
            tp->tv_nsec = 0;
        }
    }

    nsec += tp->tv_nsec;
    PRINT_DEBUG("getTimespec: current time sec = %ld, time = %ld, nsec = %ld, total nsec = %lld\n",
           tp->tv_sec, time(NULL) + 1, tp->tv_nsec, nsec);
#endif

    sec = 0;
    if (nsec >= NSEC2SEC)
    {
        sec = nsec / NSEC2SEC;
        nsec = nsec % NSEC2SEC;
    }
    tp->tv_sec += sec;
    tp->tv_nsec = nsec;
    PRINT_DEBUG("getTimespec: after time sec = %ld, time = %ld, nsec = %ld\n",
           tp->tv_sec, time(NULL) + 1, tp->tv_nsec);

    return;
}

/**@brief  获取系统线程最大和最小优先级
 * @param  pMinPrio  最小优先级
 * @param  pMaxPrio  最大优先级
 * @return 成功返回 0
 * @return 错误返回 其他
 * @see
 * @note
 */
int sys_pthread_priority_get(int *pMinPrio, int *pMaxPrio)
{
    /* get the allowable priority range for the scheduling policy */
    if (pMinPrio != NULL)
    {
        (*pMinPrio) = sched_get_priority_min(SCHED_RR);
        if (*pMinPrio == -1)
        {
            return -1;
        }
    }
    if (pMaxPrio != NULL)
    {
        (*pMaxPrio) = sched_get_priority_max(SCHED_RR);
        if (*pMaxPrio == -1)
        {
            return -1;
        }
    }

    return 0;
}

/**@brief  设置线程状态属性
 * @param  attr     线程属性指针
 * @param  priority 任务优先级
 * @param  stacksize 创建任务栈大小
 * @return 成功返回 0
 * @return 错误返回 其他
 * @see
 * @note
 */
static int sys_pthread_attr(pthread_attr_t *attr, int priority, size_t *stacksize)
{
    int rval;
    struct sched_param params;
    int maxPriority, minPriority;

    rval = pthread_attr_init(attr);
    if (rval != 0)
    {
        return rval;
    }

    /* use the round robin scheduling algorithm */
    //设置线程调度模式，循环
    rval = pthread_attr_setschedpolicy(attr, SCHED_RR);
    if (rval != 0)
    {
        pthread_attr_destroy(attr);
        return rval;
    }

    /* set the thread to be detached */
    //设置为分离线程，让线程结束的话释放自己的资源
    rval = pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED);
    if (rval != 0)
    {
        pthread_attr_destroy(attr);
        return rval;
    }

    /* first get the scheduling parameter, then set the new priority */
    //设置线程的优先级
    rval = pthread_attr_getschedparam(attr, &params);
    if (rval != 0)
    {
        pthread_attr_destroy(attr);
        return rval;
    }

    rval = sys_pthread_priority_get(&minPriority, &maxPriority);
    if (rval != 0)
    {
        pthread_attr_destroy(attr);
        return rval;
    }
    if (priority < minPriority)
    {
        priority = minPriority;
    }
    else if (priority > maxPriority)
    {
        priority = maxPriority;
    }
    params.sched_priority = priority;
    rval = pthread_attr_setschedparam(attr, &params);
    if (rval != 0)
    {
        pthread_attr_destroy(attr);
        return rval;
    }

#ifdef USE_NPTL
    /* nptl创建线程默认继承父线程的调度优先级，需要设置inheritsched为
       PTHREAD_EXPLICIT_SCHED，自己设置的调度策略和优先级才能生效 */
    rval = pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
    if (rval != 0)
    {
        pthread_attr_destroy(attr);
        return rval;
    }
#endif

    /* when set stack size, we define a minmum value to avoid fail */
    //设置栈大小，最小8192
    if (*stacksize < PTHREAD_STACK_MIN)
    {
        *stacksize = PTHREAD_STACK_MIN;
    }

#ifdef USE_NPTL
    /*  hisi3531平台使用nptl线程库，部分线程会因为设置的堆栈大小不够大导致程序
        内存不足崩掉，暂时将所有线程堆栈大小设置成1M */
    stacksize = 1 * 1024 * 1024;
#endif

    rval = pthread_attr_setstacksize(attr, *stacksize);
    if (rval != 0)
    {
        pthread_attr_destroy(attr);
        return rval;
    }
    return 0;
}

/**@brief  创建线程
 * @param  pTid      任务ID句柄指针
 * @param  pTaskName 任务名,最大长度16，且必须为静态或全局变量
 * @param  priority  任务优先级
 * @param  stacksize 创建任务栈大小
 * @param  funcptr   创建任务函数
 * @param  args      参数个数
 * @return 成功返回 0
 * @return 错误返回 其他
 * @see
 * @note
 */
UINT32 sys_pthread_create(TASK_ID *pTid, CHAR *pTaskName, UINT32 priority, UINT32 stacksize,
                          void *(*funcptr)(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *),
                          unsigned args, ...)
{
    int i, rval;
    void *arg[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
    pthread_t tid, *raw;
    pthread_attr_t attr;
    va_list ap;
    if (stacksize < 32 * 1024)
    {
        stacksize = 32 * 1024;
    }
    size_t actStackSize = stacksize;

    if (funcptr == NULL || args > 10)
    {
        return EINVAL;
    }

    if (pTaskName != NULL)
    {
        PRINT_DEBUG(" --> TaskName = [%s], priority = [%d], stacksize = [%d] \n", pTaskName, priority, stacksize);
    }

    va_start(ap, args);
    for (i = 0; i < args; i++)
    {
        arg[i] = va_arg(ap, void *);
        
    }
    va_end(ap);

    rval = sys_pthread_attr(&attr, priority, &actStackSize);
    if (rval != 0)
    {
        PRINT_DEBUG("setPthreadAttr failed %d,sys errno %d\n", rval, errno);
        return rval;
    }

    if (pTid != NULL)
    {
        raw = pTid;
    }
    else
    {
        raw = &tid;
    }
   
    if (args <= 1)
    {
        FUNC_WRAPPER3 *pstFunc = NULL;
        pstFunc = (FUNC_WRAPPER3 *)malloc(sizeof(FUNC_WRAPPER3));
        if (pstFunc == NULL)
        {
            (void)pthread_attr_destroy(&attr);
            return ENOMEM;
        }
        pstFunc->entry = (FUNCPTR)funcptr;
        pstFunc->arg[0] = arg[0];
        pstFunc->pName = pTaskName;
        rval = pthread_create(raw, &attr, sys_thread_wrapper, (void *)pstFunc);
        if (rval != 0)
        {
            free((char *)pstFunc);
        }
    }
    else
    {
        FUNC_WRAPPER3 *pstFunc = NULL;
        pstFunc = (FUNC_WRAPPER3 *)malloc(sizeof(FUNC_WRAPPER3));
        if (pstFunc == NULL)
        {
            (void)pthread_attr_destroy(&attr);
            return ENOMEM;
        }
        memset((char *)pstFunc, 0, sizeof(FUNC_WRAPPER3));
        pstFunc->entry = (FUNCPTR)funcptr;
        pstFunc->pName = pTaskName;
        for (i = 0; i < args; i++)
        {
            pstFunc->arg[i] = arg[i];
        }
        rval = pthread_create(raw, &attr, sys_thread_wrapper, (void *)pstFunc);
        if (rval != 0)
        {
            free((char *)pstFunc);
        }
    }

    pthread_attr_destroy(&attr);
    PRINT_DEBUG("pthread create: tid = %d, priority = %d, stacksize = %ld\n", (int)*raw, priority, actStackSize);
    return rval;
}

/**@brief  取消线程
 * @param  uId      任务ID句柄
 * @return 成功返回 0
 * @return 错误返回 其他
 * @see
 * @note
 */
UINT32 sys_pthread_cancel(TASK_ID *uId)
{
#ifdef USE_NPTL
    if (*uId == (TASK_ID)-1 || *uId == (TASK_ID)0)
    {
        return -1;
    }
#endif
    return pthread_cancel(*uId);
}

/**@brief  验证线程是否存在
 * @param  uId     任务ID句柄
 * @return 成功返回 0
 * @return 错误返回 其他
 * @see
 * @note
 */
UINT32 sys_pthread_verify(TASK_ID *uId)
{
    if (*uId == (TASK_ID)-1 || *uId == (TASK_ID)0)
    {
        return -1;
    }

    return pthread_kill(*uId, 0);
}

/**
 * @brief   验证线程是否存在	 
 * @param   [in] pTid 任务ID句柄指针
 * @return  存在返回OK
 * @return  其他返回ERROR
 */
INT32 sys_pthread_exist(TASK_ID *pTid)
{
    INT32 iRet;

    if (pTid == NULL)
    {
        PRINT_DEBUG("pTid invalid ptr.\n");
        return ERROR;
    }

    if (*pTid == (TASK_ID)-1 || *pTid == (TASK_ID)0)
    {
        return -1;
    }

    iRet = pthread_kill(*pTid, 0);
    if (iRet != OK)
    {
        iRet = ERROR;
    }

    return iRet;
}

/**@brief  阻塞线程
 * @param  uId      任务ID句柄
 * @return 成功返回 0
 * @return 错误返回 其他
 * @see
 * @note
 */
UINT32 sys_pthread_suspend(TASK_ID *uId)
{
    return pthread_kill(*uId, SIGSTOP);
}

/**@brief  恢复阻塞的线程
 * @param  uId      任务ID句柄
 * @return 成功返回 0
 * @return 错误返回 其他
 * @see
 * @note
 */
UINT32 sys_pthread_reume(TASK_ID *uId)
{
    return pthread_kill(*uId, SIGCONT);
}

/**@brief  阻塞线程
 * @param  msec   阻塞时长
 * @return 成功返回 0
 * @return 错误返回 -1
 * @see
 * @note
 */
UINT32 sys_wait_msec(UINT32 uMsec)
{
    return usleep(uMsec * 1000);
}

/**@brief  创建互斥锁
 * @param  pMux     互斥锁句柄指针
 * @param  iType    互斥锁类型
 * @return 成功返回 0
 * @return 错误返回 其他
 * @see
 * @note
 */
UINT32 sys_mutex_create(MUTEX_ID *pstMutex, INT32 iType)
{
    pthread_mutexattr_t attr;

    if (pstMutex == NULL)
    {
        PRINT_DEBUG("Mutex init invalid ptr. \n");
        return ERROR;
    }

    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, iType);

    return pthread_mutex_init(pstMutex, &attr);
}

/**@brief  加锁
 * @param  pMux     互斥锁句柄指针
 * @param  iWaitMs  等待时间
 * @return 成功返回 0
 * @return 错误返回 其他
 * @see
 * @note
 */
UINT32 sys_mutex_lock(MUTEX_ID *pstMutex, INT32 iWaitMs)
{
    INT32 iRet = 0;
    struct timespec absTime = {0};

    if (pstMutex == NULL)
    {
        PRINT_DEBUG("Mutex lock invalid ptr. \n");
        return ERROR;
    }

    if (iWaitMs == NO_WAIT)
    {
        iRet = pthread_mutex_trylock(pstMutex);
    }
    else if (iWaitMs == WAIT_FOREVER)
    {
        iRet = pthread_mutex_lock(pstMutex);
    }
    else
    {
        sys_timespec_get(iWaitMs, &absTime);
        iRet = pthread_mutex_timedlock(pstMutex, &absTime);
    }

    return iRet;
}

/**@brief  解锁
 * @param  pMux     互斥锁句柄指针
 * @return 成功返回 0
 * @return 错误返回 其他
 * @see
 * @note
 */
UINT32 sys_mutex_unlock(MUTEX_ID *pstMutex)
{
    if (pstMutex == NULL)
    {
        PRINT_DEBUG("Mutex unlock invalid ptr. \n");
        return ERROR;
    }

    return pthread_mutex_unlock(pstMutex);
}

/**@brief  销毁锁句柄
 * @param  pMux     互斥锁句柄指针
 * @return 成功返回 0
 * @return 错误返回 其他
 * @see
 * @note
 */
UINT32 sys_mutex_destroy(MUTEX_ID *pstMutex)
{
    if (pstMutex == NULL)
    {
        PRINT_DEBUG("Mutex destroy invalid ptr. \n");
        return ERROR;
    }

    return pthread_mutex_destroy(pstMutex);
}

/**@brief  创建一个消息队列
 * @param  pName     消息队列名字
 * @param  iMaxMsg   一个消息队列最大消息个数
 * @param  iMsgLen   消息长度
 * @return 成功返回 消息队列句柄
 * @return 错误返回 其他
 * @see
 * @note
 */
UINT32 sys_mqueue_create(MSG_Q_ID *pstHandle, const CHAR *pName, INT32 iMaxMsg, INT32 iMsgLen)
{
    CHAR sName[64] = {0};
    MSG_Q_ATTR stAttr = {0};
    MSG_Q_ID stMqId;

    if (pName == NULL || pstHandle == NULL)
    {
        PRINT_DEBUG("Mqueue open invalid ptr.\n");
        return ERROR;
    }

    stAttr.mq_maxmsg = iMaxMsg;
    snprintf(sName, sizeof(sName), "/%s", pName);
    stAttr.mq_msgsize = iMsgLen;

    mq_unlink(sName);

    stMqId = mq_open(sName, MSG_Q_OFLAG, MSG_Q_MODE, &stAttr);
    if (stMqId == ERROR)
    {
        PRINT_DEBUG("Mqueue %s open failed err:%s.\n", sName, strerror(errno));
        return ERROR;
    }

    *pstHandle = stMqId;

    return OK;
}

/**@brief  发送消息
 * @param  mqId     消息队列句柄
 * @param  pMsg     发送的消息
 * @param  uLen     消息长度
 * @param  iWaitMs  阻塞时长
 * @return 成功返回 0
 * @return 错误返回 其他
 * @see
 * @note
 */
UINT32 sys_mqueue_send(MSG_Q_ID *mqId, const CHAR *pMsg, UINT32 uLen, INT32 iWaitMs)
{
    INT32 iRet = 0;
    struct timespec absTime = {0};

    if (pMsg == NULL || mqId == NULL)
    {
        PRINT_DEBUG("Mqueue send invalid ptr.\n");
        return ERROR;
    }

    if (iWaitMs == WAIT_FOREVER)
    {
        iRet = mq_send(*mqId, pMsg, uLen, MSG_Q_PRIO);
    }
    else
    {
        /*TODO J6平台设置超时时间在校时时会导致等待时间和预计的不一致*/
        sys_timespec_get(iWaitMs, &absTime);
        iRet = mq_timedsend(*mqId, pMsg, uLen, MSG_Q_PRIO, &absTime);
    }

    return iRet;
}

/**@brief  接受消息
 * @param  mqId     消息队列句柄
 * @param  pMsg     接受消息缓存
 * @param  uLen     缓存大小
 * @param  iWaitMs  阻塞时长
 * @return 成功返回 接收的字节数
 * @return 错误返回 ERROR
 * @see
 * @note
 */
UINT32 sys_mqueue_recv(MSG_Q_ID *mqId, CHAR *pMsgBuf, INT32 iBufLen, INT32 iWaitMs)
{
    INT32 iRet = 0;
    struct timespec absTime = {0};

    if (pMsgBuf == NULL || mqId == NULL)
    {
        PRINT_DEBUG("Mqueue send invalid ptr.\n");
        return ERROR;
    }

    if (WAIT_FOREVER == iWaitMs)
    {
        iRet = mq_receive(*mqId, pMsgBuf, iBufLen, MSG_Q_PRIO);
    }
    else if (NO_WAIT == iWaitMs)
    {
        memset(&absTime, 0, sizeof(absTime));
        iRet = mq_timedreceive(*mqId, pMsgBuf, iBufLen, MSG_Q_PRIO, &absTime);
    }
    else
    {
#if 1
        sys_timespec_get(iWaitMs, &absTime);
        iRet = mq_timedreceive(*mqId, pMsgBuf, iBufLen, MSG_Q_PRIO, &absTime);
#else
        /*J6平台设置超时时间在校时时会导致等待时间和预计的不一致*/
        PRINT_DEBUG("mqueue not support timeout recv, please use NO_WAIT!\n");
        return ERROR;
#endif
    }

    return iRet;
}

/**@brief  关闭消息队列
 * @param  mqId     消息队列句柄
 * @return 成功返回 0
 * @return 错误返回 其他
 * @see
 * @note
 */
UINT32 sys_mqueue_close(MSG_Q_ID *mqId)
{
    return mq_close(*mqId);
}

/**@brief  创建信号量
 * @param  semId   信号量句柄指针
 * @param  uVal    信号量初始值
 * @return 成功返回 0
 * @return 错误返回 其他
 * @see
 * @note
 */
UINT32 sys_sem_create(SEM_ID *pstSem, UINT32 uVal)
{
    if (pstSem == NULL)
    {
        PRINT_DEBUG("Sem init invalid ptr.\n");
        return ERROR;
    }

    return sem_init(pstSem, 0, uVal);
}

/**@brief  释放一个信号量资源
 * @param  semId   信号量句柄指针
 * @return 成功返回 0
 * @return 错误返回 其他
 * @see
 * @note
 */
UINT32 sys_sem_post(SEM_ID *semId)
{
    if (semId == NULL)
    {
        PRINT_DEBUG("Sem post invalid ptr.\n");
        return ERROR;
    }

    return sem_post(semId);
}

/**@brief  获取一个信号量资源
 * @param  semId   信号量句柄指针
 * @param  uWaitMs 阻塞时长
 * @return 成功返回 0
 * @return 错误返回 其他
 * @see
 * @note
 */
UINT32 sys_sem_wait(SEM_ID *pstSem, UINT32 uWaitMs)
{
    INT32 iRet = 0;
    struct timespec absTime = {0};

    if (pstSem == NULL)
    {
        PRINT_DEBUG("Sem wait invalid ptr.\n");
        return ERROR;
    }

    if (uWaitMs == NO_WAIT)
    {
        iRet = sem_trywait(pstSem);
    }
    else if (uWaitMs == WAIT_FOREVER)
    {
        iRet = sem_wait(pstSem);
    }
    else
    {
        sys_timespec_get(uWaitMs, &absTime);
        iRet = sem_timedwait(pstSem, &absTime);
    }
    return iRet;
}

/**@brief  销毁信号量
 * @param  semId   信号量句柄指针
 * @return 成功返回 0
 * @return 错误返回 其他
 * @see
 * @note
 */
UINT32 sys_sem_destroy(SEM_ID *pstSem)
{
    if (pstSem == NULL)
    {
        PRINT_DEBUG("Sem destroy invalid ptr.\n");
        return ERROR;
    }

    return sem_destroy(pstSem);
}
